package org.colour.amber.rest;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * @author jasonfu
 * @date 2024/9/25 09:48
 */
@Slf4j
@RestController
@RequestMapping(ConfigV1RestController.PATH_CONFIG_V1)
public class ConfigV1RestController {

    public static final String PATH_CONFIG_V1 = "/api/v1/config";

    // guava 提供的多值 Map，一个 key 可以对应多个 value
    private final Multimap<String, AsyncTask> dataIdContext = Multimaps.synchronizedSetMultimap(HashMultimap.create());

    private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("longPolling-timeout-checker-%d")
            .build();
    private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1, threadFactory);

    // 配置监听接入点
    @GetMapping("/{configId}/listener")
    public void addListener(
            @PathVariable("configId") String configId, HttpServletRequest request, HttpServletResponse response) {
        // 开启异步
        AsyncContext asyncContext = request.startAsync(request, response);
        AsyncTask asyncTask = new AsyncTask(asyncContext, true);

        // 维护 configId 和异步请求上下文的关联
        dataIdContext.put(configId, asyncTask);

        // 启动定时器，10s 后写入 304 响应
        executor.schedule(
                () -> {
                    if (asyncTask.isTimeout()) {
                        dataIdContext.remove(configId, asyncTask);
                        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                        asyncContext.complete();
                    }
                },
                10000,
                TimeUnit.MILLISECONDS);
    }

    // 配置发布接入点
    @PostMapping
    @SneakyThrows
    public String publishConfig(
            @RequestParam(name = "configId") String configId, @RequestParam(name = "configValue") String configValue) {
        log.info("publish configInfo dataId: [{}], configInfo: {}", configId, configValue);
        Collection<AsyncTask> asyncTasks = dataIdContext.removeAll(configId);
        for (AsyncTask asyncTask : asyncTasks) {
            asyncTask.setTimeout(false);
            HttpServletResponse response =
                    (HttpServletResponse) asyncTask.getAsyncContext().getResponse();
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().println(configValue);
            asyncTask.getAsyncContext().complete();
        }
        return "success";
    }

    @Data
    private static class AsyncTask {
        // 长轮询请求的上下文，包含请求和响应体
        private AsyncContext asyncContext;
        // 超时标记
        private boolean timeout;

        public AsyncTask(AsyncContext asyncContext, boolean timeout) {
            this.asyncContext = asyncContext;
            this.timeout = timeout;
        }
    }
}
